#!/usr/bin/env python
# -*- coding: utf-8 -*-


import os, os.path, sys
import md5
import types

import tempfile
import unittest

from potherb import log
from potherb.tablet import TabletMutator
from potherb.const import ROW_NOT_FOUND,ROW_FOUND,ROW_DELETED
from potherb.const import COLUMN_NOT_FOUND,COLUMN_FOUND,COLUMN_DELETED
from potherb.const import OP_DONE, OP_STOP

DASH = '-'

def makeData(key) :
    return DASH.join([key] * 4)
    # makeData()


class   TestTabletMutator(unittest.TestCase) :
    def setUp(self) :
        self.store_dir = tempfile.mkdtemp(suffix='.dir', prefix='tablet_')
        log.debug('store_dir=%s', self.store_dir)

        self.tablet_name = 'tablet.test'
        self.tablet = TabletMutator(self.tablet_name, self.store_dir)
        ## setUp()

    def removeDirs(self, dir_name) :
        items = os.listdir(dir_name)
        for item in items :
            dir_item = '%s/%s' % (dir_name, item)
            if os.path.isdir(dir_item) :
                self.removeDirs(dir_item)
            else :
                #log.debug('CONTENT of file %s', dir_item)
                #for line in file(dir_item) :
                #    log.debug('%s', line.strip())
                log.debug('remove file %s', dir_item)
                os.remove(dir_item)
        if os.path.exists(dir_name) :
            log.debug('remove directory %s', dir_name)
            os.removedirs(dir_name)
        ## removedirs()

    def tearDown(self) :
        del self.tablet

        if os.path.exists(self.store_dir) :
            self.removeDirs(self.store_dir)
        ## tearDown()

    ####-----------------------------------------------------------####

    def test_insert_001(self) :
        '''test INSERT operation, and assert find the data inserted
        '''
        tablet = self.tablet
        
        records = (('foo',10,'ubuntu'), ('baz',32,'fedora'), ('bar',15,'debian')) 
        timestamp = 1000
        for row_key,age,release in records :
            columns = [('age','',age), ('release','',release)]
            tablet.insert(row_key, columns, timestamp=timestamp)

            timestamp += 1

        index = 0
        timestamp = 1000
        for row_key in ('foo', 'baz', 'bar') :
            columns = [('age','',None), ('release','',None)]
            columns_found, columns_not_found = tablet.findColumn(row_key, columns)
            self.assertEqual(2, len(columns_found))
            self.assertEqual(0, len(columns_not_found))

            # ASSERT columns-FOUND
            record = records[index]
            column_index = 0
            for _family,_qualifier,_data,_timestamp in columns_found :
                column = columns[column_index]
                self.assertEqual(_family, column[0])
                self.assertEqual(_qualifier, column[1])
                data = record[ 1 + column_index ]
                if not isinstance(data, types.StringType) : data = str(data)
                self.assertEqual(_data, data)
                self.assertEqual(_timestamp, timestamp)

                column_index += 1

            index += 1
            timestamp += 1

        for row_key in ('fooN', 'bazN', 'barN') :
            columns = [('age','',None), ('release','',None)]
            columns_found, columns_not_found = tablet.findColumn(row_key, columns)
            self.assertEqual(0, len(columns_found))
            self.assertEqual(2, len(columns_not_found))

            # ASSERT columns-NOT-FOUND
            column_index = 0
            for _family,_qualifier,_data,_timestamp in columns_not_found :
                column = columns[column_index]
                self.assertEqual(_family, column[0])
                self.assertEqual(_qualifier, column[1])
                self.assertEqual(_data, None)
                self.assertEqual(_timestamp, None)

                column_index += 1
        ## test_insert_001()

    def test_insert_002(self) :
        '''ERASE after INSERT operation, and assert cannot find the data inserted
        '''
        tablet = self.tablet
        
        records = (('foo',10,'ubuntu'), ('baz',32,'fedora'), ('bar',15,'debian')) 
        timestamp = 1000
        for row_key,age,release in records :
            columns = [('age','',age), ('release','',release)]
            tablet.insert(row_key, columns, timestamp=timestamp)

            timestamp += 1

        index = 0
        timestamp = 1000
        for row_key in ('foo', 'baz', 'bar') :
            columns = [('age','',None), ('release','',None)]
            columns_found, columns_not_found = tablet.findColumn(row_key, columns)
            self.assertEqual(2, len(columns_found))
            self.assertEqual(0, len(columns_not_found))

            # ASSERT columns-FOUND
            record = records[index]
            column_index = 0
            for _family,_qualifier,_data,_timestamp in columns_found :
                column = columns[column_index]
                self.assertEqual(_family, column[0])
                self.assertEqual(_qualifier, column[1])
                data = record[ 1 + column_index ]
                if not isinstance(data, types.StringType) : data = str(data)
                self.assertEqual(_data, data)
                self.assertEqual(_timestamp, timestamp)

                column_index += 1

            index += 1
            timestamp += 1

        # test ERASE column 'age'  without timestamp
        for row_key in ('foo', 'baz', 'bar') :
            columns_to_erase = [('age','',None),]
            columns_erased = tablet.erase(row_key, columns_to_erase)
            self.assertEqual(1, len(columns_erased))

            # ASSERT STATUS of columns-erased
            column_index = 0
            for _family,_qualifier,_status, in columns_erased :
                column = columns_to_erase[column_index]
                self.assertEqual(_family, column[0])
                self.assertEqual(_qualifier, column[1])
                self.assertEqual(_status, COLUMN_DELETED)

                column_index += 1

        # after ERASE column 'age', ASSERT column 'age' can NOT be FOUND
        for row_key in ('foo', 'baz', 'bar') :
            columns = [('age','',None), ]
            columns_found, columns_not_found = tablet.findColumn(row_key, columns)
            self.assertEqual(0, len(columns_found))
            self.assertEqual(1, len(columns_not_found))

        # after ERASE column 'age', ASSERT column 'release' can be FOUND
        index = 0
        timestamp = 1000
        for row_key in ('foo', 'baz', 'bar') :
            columns = [('release','',None), ]
            columns_found, columns_not_found = tablet.findColumn(row_key, columns)
            self.assertEqual(1, len(columns_found))
            self.assertEqual(0, len(columns_not_found))

            # ASSERT columns 'release' FOUND
            record = records[index]
            for _family,_qualifier,_data,_timestamp in columns_found :
                column = columns[0]
                self.assertEqual(_family, column[0])
                self.assertEqual(_qualifier, column[1])
                data = record[ 2 ]
                if not isinstance(data, types.StringType) : data = str(data)
                self.assertEqual(_data, data)
                self.assertEqual(_timestamp, timestamp)

            index += 1
            timestamp += 1

        ## test_insert_002()

    def test_insert_003(self) :
        '''test INSERT IGNORE operation 
        '''
        tablet = self.tablet
        
        records = (('foo',10,'ubuntu'), ('baz',32,'fedora'), ('bar',15,'debian')) 
        timestamp = 1000
        for tries in range(5) :
            for row_key,age,release in records :
                columns = [('age','',age), ('release','',release)]
                status,columns_error = tablet.insert(row_key, columns, timestamp=timestamp)
                if tries > 0 :
                    self.assertEqual(OP_STOP, status)
                else :
                    self.assertEqual(OP_DONE, status)

                timestamp += 1

        index = 0
        timestamp = 1000
        for row_key in ('foo', 'baz', 'bar') :
            columns = [('age','',None), ('release','',None)]
            columns_found, columns_not_found = tablet.findColumn(row_key, columns)
            self.assertEqual(2, len(columns_found))
            self.assertEqual(0, len(columns_not_found))

            # ASSERT columns-FOUND
            record = records[index]
            column_index = 0
            for _family,_qualifier,_data,_timestamp in columns_found :
                column = columns[column_index]
                self.assertEqual(_family, column[0])
                self.assertEqual(_qualifier, column[1])
                data = record[ 1 + column_index ]
                if not isinstance(data, types.StringType) : data = str(data)
                self.assertEqual(_data, data)
                self.assertEqual(_timestamp, timestamp)

                column_index += 1

            index += 1
            timestamp += 1
        ## test_insert_003()

    ####-----------------------------------------------------------####

    def test_update_001(self) :
        '''test UPDATE operation, and assert find the data inserted
        '''
        tablet = self.tablet
        
        records = (('foo',10,'ubuntu'), ('baz',32,'fedora'), ('bar',15,'debian')) 
        # try to UPDATE
        timestamp = 1000
        for row_key,age,release in records :
            columns = [('age','',age), ('release','',release)]
            status,columns_error = tablet.update(row_key, columns, timestamp=timestamp)
            self.assertEqual(OP_STOP, status)
            self.assertEqual(2, len(columns_error))

            timestamp += 1

        # try to INSERT
        timestamp = 1000
        for row_key,age,release in records :
            columns = [('age','',age), ('release','',release)]
            status,columns_error = tablet.insert(row_key, columns, timestamp=timestamp)
            self.assertEqual(OP_DONE, status)
            self.assertEqual(None, columns_error)

            timestamp += 1

        # VERIFY after INSERTION
        index = 0
        timestamp = 1000
        for row_key in ('foo', 'baz', 'bar') :
            columns = [('age','',None), ('release','',None)]
            columns_found, columns_not_found = tablet.findColumn(row_key, columns)
            self.assertEqual(2, len(columns_found))
            self.assertEqual(0, len(columns_not_found))

            # ASSERT columns-FOUND
            record = records[index]
            column_index = 0
            for _family,_qualifier,_data,_timestamp in columns_found :
                column = columns[column_index]
                self.assertEqual(_family, column[0])
                self.assertEqual(_qualifier, column[1])
                data = record[ 1 + column_index ]
                if not isinstance(data, types.StringType) : data = str(data)
                self.assertEqual(_data, data)
                self.assertEqual(_timestamp, timestamp)

                column_index += 1

            index += 1
            timestamp += 1

        # try to UPDATE, again
        timestamp = 2000
        for row_key,age,release in records :
            columns = [('age','',age), ('release','',release)]
            status,columns_error = tablet.update(row_key, columns, timestamp=timestamp)
            self.assertEqual(OP_DONE, status)
            self.assertEqual(None, columns_error)

            timestamp += 1

        # VERIFY after UPDATION
        index = 0
        timestamp = 2000
        for row_key in ('foo', 'baz', 'bar') :
            columns = [('age','',None), ('release','',None)]
            columns_found, columns_not_found = tablet.findColumn(row_key, columns)
            self.assertEqual(2, len(columns_found))
            self.assertEqual(0, len(columns_not_found))

            # ASSERT columns-FOUND
            record = records[index]
            column_index = 0
            for _family,_qualifier,_data,_timestamp in columns_found :
                column = columns[column_index]
                self.assertEqual(_family, column[0])
                self.assertEqual(_qualifier, column[1])
                data = record[ 1 + column_index ]
                if not isinstance(data, types.StringType) : data = str(data)
                self.assertEqual(_data, data)
                self.assertEqual(_timestamp, timestamp)

                column_index += 1

            index += 1
            timestamp += 1

        ## test_update_001()

    ####-----------------------------------------------------------####

    def test_upsert_001(self) :
        '''test UPSERT operation, and assert find the data inserted
        '''
        tablet = self.tablet
        
        records = (('foo',10,'ubuntu'), ('baz',32,'fedora'), ('bar',15,'debian')) 
        # try to UPDATE
        timestamp = 1000
        for row_key,age,release in records :
            columns = [('age','',age), ('release','',release)]
            status,columns_error = tablet.update(row_key, columns, timestamp=timestamp)
            self.assertEqual(OP_STOP, status)
            self.assertEqual(2, len(columns_error))

            timestamp += 1

        # try to UPSERT
        timestamp = 1000
        for row_key,age,release in records :
            columns = [('age','',age), ('release','',release)]
            status,columns_error = tablet.upsert(row_key, columns, timestamp=timestamp)
            self.assertEqual(OP_DONE, status)
            self.assertEqual(None, columns_error)

            timestamp += 1

        # VERIFY after UPNSERTION
        index = 0
        timestamp = 1000
        for row_key in ('foo', 'baz', 'bar') :
            columns = [('age','',None), ('release','',None)]
            columns_found, columns_not_found = tablet.findColumn(row_key, columns)
            self.assertEqual(2, len(columns_found))
            self.assertEqual(0, len(columns_not_found))

            # ASSERT columns-FOUND
            record = records[index]
            column_index = 0
            for _family,_qualifier,_data,_timestamp in columns_found :
                column = columns[column_index]
                self.assertEqual(_family, column[0])
                self.assertEqual(_qualifier, column[1])
                data = record[ 1 + column_index ]
                if not isinstance(data, types.StringType) : data = str(data)
                self.assertEqual(_data, data)
                self.assertEqual(_timestamp, timestamp)

                column_index += 1

            index += 1
            timestamp += 1

        # try to UPSERT, again
        timestamp = 2000
        for row_key,age,release in records :
            columns = [('age','',age), ('release','',release)]
            status,columns_error = tablet.upsert(row_key, columns, timestamp=timestamp)
            self.assertEqual(OP_DONE, status)
            self.assertEqual(None, columns_error)

            timestamp += 1

        # VERIFY after UPSERTION
        index = 0
        timestamp = 2000
        for row_key in ('foo', 'baz', 'bar') :
            columns = [('age','',None), ('release','',None)]
            columns_found, columns_not_found = tablet.findColumn(row_key, columns)
            self.assertEqual(2, len(columns_found))
            self.assertEqual(0, len(columns_not_found))

            # ASSERT columns-FOUND
            record = records[index]
            column_index = 0
            for _family,_qualifier,_data,_timestamp in columns_found :
                column = columns[column_index]
                self.assertEqual(_family, column[0])
                self.assertEqual(_qualifier, column[1])
                data = record[ 1 + column_index ]
                if not isinstance(data, types.StringType) : data = str(data)
                self.assertEqual(_data, data)
                self.assertEqual(_timestamp, timestamp)

                column_index += 1

            index += 1
            timestamp += 1

        ## test_upsert_001()

    ## class TestTabletMutator

#######################################################################

def test_suite():
    suite = unittest.TestSuite()

    suite.addTest(unittest.makeSuite(TestTabletMutator))

    return suite

if  __name__ == '__main__' :
    #log.setLogPath('/dev/null')
    log.enableDebug()

    unittest.main()

